#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<unistd.h>
#include<cstring>
#include<sys/types.h>
#include<sys/wait.h>

enum
{
    OK=0,
    EXECUTE_FAIL,
    INCORRECT_COMMAND_LINE,
};
int Lastcode=0;
const int basesize=1024;
const int argvnum=64;
//全局的命令行参数列表
char*gargv[argvnum];
int gargc=0;

//全局的环境变量列表
char*genv[argvnum];
int genvc=0;

//当前路径
char pwd[basesize];
char pwdenv[basesize];

//接口，获取用户名，倘若不存在或者获取失败就返回None
std::string GetUsername()
{
    std::string username=getenv("USER");
    return username.empty()?"None":username;
}

//接口，获取主机名，倘若不存在或者获取失败就返回None
std::string GetHostname()
{
    char hostname[basesize];
    if(gethostname(hostname,sizeof(hostname))!=0)
    {
        return "None";
    }
    hostname[sizeof(hostname)-1]='\0';
    return hostname;
}


void InitEnv()
{
    extern char**environ;
    int index=0;
    while(environ[index])
    {
        genv[index]=(char*)malloc(strlen(environ[index])+1);
        strncpy(genv[index],environ[index],strlen(environ[index])+1);
        index++;
    }
    genv[index]=nullptr;
}
//接口，获取当前路径，倘若不存在或者获取失败就返回None
std::string Getcwd()
{
    if(nullptr == getcwd(pwd, sizeof(pwd))) return "None";
    snprintf(pwdenv, sizeof(pwdenv),"PWD=%s", pwd);
    putenv(pwdenv);
    return pwd;
}

std::string LastDir()
{
    std::string curr = Getcwd();
    if(curr == "/" || curr == "None") return curr;
    size_t pos = curr.rfind("/");
    if(pos == std::string::npos) return curr;
    return curr.substr(pos+1);
}

//接口，负责生成完整的命令行并返回
std::string MakeCommandLine()
{
    char command_line[basesize];
    snprintf(command_line,basesize,"%s@%s:%s$",GetUsername().c_str(),GetHostname().c_str(),LastDir().c_str());
    return command_line;
}

//打印命令行并刷新缓冲区
void PrintCommandLine()
{
    printf("%s",MakeCommandLine().c_str());
    fflush(stdout);
}

bool  GetCommandLine(char Command_buffer[],int size)
{
    //我们这里选择使用fgets来读取一行的字符串，可能包含:ls -l -a等
    char *result=fgets(Command_buffer,size,stdin);
    if(!result)
    {
        return false;
    }
    //fgets会将换行符也读取进来，我们需要将其替换为\0
    size_t len=strlen(Command_buffer);
    if(len==0)
    {
        return false;
    }
    Command_buffer[len-1]='\0';
    return true;
}

void  ParseCommandLine(char Command_buffer[])
{
    //每次分析前我们都要重置一下，防止被历史堆积数据影响
    memset(gargv,0,sizeof(gargv));
    gargc=0;

    //我们这里采用strtok来进行切分
    gargv[gargc++]=strtok(Command_buffer," ");
    while(gargv[gargc++]=strtok(nullptr," "));
    //因为我们的while判断条件一定会多执行一次，所以我们后面要把减一
    gargc--;
}

bool ExecuteCommandLine()
{
    pid_t id = fork();

    if(id==0)
    {
        //子进程代码
        execvpe(gargv[0], gargv, genv);
        exit(INCORRECT_COMMAND_LINE);//非正常退出，因为exec之后只有失败才会执行到这里
    }

    int status;
    int rid=waitpid(id,&status,0);
    if(rid>0)
    {
        if(WIFEXITED(status))
        {
            Lastcode=WEXITSTATUS(status);
            return true;
        }
        else
        {
            Lastcode=EXECUTE_FAIL;
            return false;
        }
    }
    Lastcode=EXECUTE_FAIL;
    return false;
}

void AddEnv(const char* item)
{
    int index=0;
    while(genv[index])
    {
        index++;
    }
    genv[index]=(char*)malloc(strlen(item)+1);
    strncpy(genv[index],item,strlen(item)+1);
    genv[++index]=nullptr;
}

//判断是否是内建命令
bool CheckAndExecBuildCommand()
{
    if(strcmp(gargv[0],"cd")==0)
    {
        if(gargc==2)
        {
            chdir(gargv[1]);
            Lastcode=OK;
        }
        else
        {
            Lastcode=INCORRECT_COMMAND_LINE;
        }
        return true;
    }
    else if(strcmp(gargv[0],"export")==0)
    {
        if(gargc==2)
        {
            AddEnv(gargv[1]);
            Lastcode=OK;
        }
        return true;
    }
    else if(strcmp(gargv[0],"env")==0)
    {
        for(int i=0;genv[i];i++)
        {
            printf("%s\n",genv[i]);
        }
        Lastcode=OK;
        return true;
    }
    else if(strcmp(gargv[0],"echo")==0)
    {
        if(gargc==2)
        {
            if(gargv[1][0]=='$')
            {
                if(gargv[1][1]=='?')
                {
                    printf("%d\n",Lastcode);
                    Lastcode=0;
                }
                return true;
            }
            else
            {
                printf("%s\n",gargv[1]);
                Lastcode=0;
                return true;
            }
        }
        else
        {
            Lastcode=INCORRECT_COMMAND_LINE;
        }
    }
    return false;
}


int main()
{
    InitEnv();
    char Command_buffer[basesize];
    while(1)
    {
        PrintCommandLine();

        if(!GetCommandLine(Command_buffer,basesize))
        {
            continue;
        }

        ParseCommandLine(Command_buffer);
        if(CheckAndExecBuildCommand())
        {
            continue;
        }
        ExecuteCommandLine();
    }
    return 0;
}